<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 4.2.1">
  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/%E6%AD%A6%E6%B1%8932x32.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/%E6%AD%A6%E6%B1%8916x16.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">


<link rel="stylesheet" href="/lib1/font-awesome/css/all.min.css">
  <link rel="stylesheet" href="/lib1/pace/pace-theme-center-simple.min.css">
  <script src="/lib1/pace/pace.min.js"></script>

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"example.com","root":"/","scheme":"Gemini","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":true,"show_result":true,"style":null},"back2top":{"enable":true,"sidebar":false,"scrollpercent":false},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":true,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},"path":"search.xml"};
  </script>

  <meta name="description" content="前言本博客参考曾探所著&lt;&lt;JavaScript设计模式与开发实践&gt;&gt;，仅为个人学习总结，侵删。 多态多态是面向对象编程的一个重要概念，在Java中，通常使用向上转型来实现多态，让两者都继承于一个统一的抽象类，之后再分别对抽象类中的方法进行不同的实现，这样就实现了多态。 而JavaScript的变量类型在运行期是可变的，一个JavaScript对象即可以表示Duck类型对象，">
<meta property="og:type" content="article">
<meta property="og:title" content="JavaScript的设计模式（一）">
<meta property="og:url" content="http://example.com/2021/10/28/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/JavaScript%E7%9A%84%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%EF%BC%88%E4%B8%80%EF%BC%89/index.html">
<meta property="og:site_name" content="Technological Blog">
<meta property="og:description" content="前言本博客参考曾探所著&lt;&lt;JavaScript设计模式与开发实践&gt;&gt;，仅为个人学习总结，侵删。 多态多态是面向对象编程的一个重要概念，在Java中，通常使用向上转型来实现多态，让两者都继承于一个统一的抽象类，之后再分别对抽象类中的方法进行不同的实现，这样就实现了多态。 而JavaScript的变量类型在运行期是可变的，一个JavaScript对象即可以表示Duck类型对象，">
<meta property="og:locale" content="zh_CN">
<meta property="article:published_time" content="2021-10-28T04:18:39.000Z">
<meta property="article:modified_time" content="2021-11-03T07:40:52.215Z">
<meta property="article:author" content="Li Yudong">
<meta property="article:tag" content="设计模式">
<meta name="twitter:card" content="summary">

<link rel="canonical" href="http://example.com/2021/10/28/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/JavaScript%E7%9A%84%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%EF%BC%88%E4%B8%80%EF%BC%89/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : true,
    lang   : 'zh-CN'
  };
</script>

  <title>JavaScript的设计模式（一） | Technological Blog</title>
  






  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">Technological Blog</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
      <p class="site-subtitle" itemprop="description">IT小白的成长之旅</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-about">

    <a href="/about/" rel="section"><i class="user fa-fw"></i>关于</a>

  </li>
        <li class="menu-item menu-item-tags">

    <a href="/tags/" rel="section"><i class="tags fa-fw"></i>标签<span class="badge">18</span></a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/categories/" rel="section"><i class="th fa-fw"></i>分类<span class="badge">14</span></a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/archives/" rel="section"><i class="archive fa-fw"></i>归档<span class="badge">95</span></a>

  </li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup">
        <div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div id="search-result">
  <div id="no-result">
    <i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>
  </div>
</div>

    </div>
  </div>

</div>
    </header>

    
  <div class="back-to-top">
    <i class="fa fa-arrow-up"></i>
    <span>0%</span>
  </div>

  <a href="https://github.com/tiarmor1" class="github-corner" title="Follow me on GitHub" aria-label="Follow me on GitHub" rel="noopener" target="_blank"><svg width="80" height="80" viewBox="0 0 250 250" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a>


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content post posts-expand">
            

    
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/10/28/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/JavaScript%E7%9A%84%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%EF%BC%88%E4%B8%80%EF%BC%89/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/author.jpg">
      <meta itemprop="name" content="Li Yudong">
      <meta itemprop="description" content="">
    </span>
    
    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Technological Blog">
    </span>
      <header class="post-header">
        <h1 class="post-title" itemprop="name headline">
          JavaScript的设计模式（一）
        </h1>
    
        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>
    
              <time title="创建时间：2021-10-28 12:18:39" itemprop="dateCreated datePublished" datetime="2021-10-28T12:18:39+08:00">2021-10-28</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2021-11-03 15:40:52" itemprop="dateModified" datetime="2021-11-03T15:40:52+08:00">2021-11-03</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/categories/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/" itemprop="url" rel="index"><span itemprop="name">设计模式</span></a>
                </span>
            </span>
    
          
    
        </div>
      </header>
    
    
    
    
    <div class="post-body" itemprop="articleBody">
    
      
        <h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>本博客参考曾探所著&lt;&lt;JavaScript设计模式与开发实践&gt;&gt;，仅为个人学习总结，侵删。</p>
<h2 id="多态"><a href="#多态" class="headerlink" title="多态"></a>多态</h2><p>多态是面向对象编程的一个重要概念，在Java中，通常使用向上转型来实现多态，让两者都继承于一个统一的抽象类，之后再分别对抽象类中的方法进行不同的实现，这样就实现了多态。</p>
<p>而JavaScript的变量类型在运行期是可变的，一个JavaScript对象即可以表示Duck类型对象，又可以便是Chicken类型对象，其多态性是与生俱来的。由此，对于JavaScript来说，某一种动物能否发出叫声，只取决于它有没有makeSound方法，而不取决于它是否是某种类型的对象。</p>
<p>多态的根本在于：你不必再向对象询问你是什么类型，而后根据答案调用对象的行为，你只用调用行为，其他的多态机制会安排妥当。其最根本作用：把过程化的条件分支语句转化为对象的多态性，从而消除这些条件分支语句。</p>
<p>多态是设计模式中的重中之重，很多设计模式的实现都离不开多态：</p>
<p>拿命令模式来说，请求被封装在一些命令对象中，这使得命令的调用者和命令的接收者可 以完全解耦开来，当调用命令的 execute 方法时，不同的命令会做不同的事情，从而会产生不同 的执行结果。而做这些事情的过程是早已被封装在命令对象内部的，作为调用命令的客户，根本 不必去关心命令执行的具体过程。  </p>
<p>在组合模式中，多态性使得客户可以完全忽略组合对象和叶节点对象之前的区别，这正是 组合模式最大的作用所在。对组合对象和叶节点对象发出同一个消息的时候，它们会各自做自己 应该做的事情，组合对象把消息继续转发给下面的叶节点对象，叶节点对象则会对这些消息作出 真实的反馈。</p>
<p> 在策略模式中，Context 并没有执行算法的能力，而是把这个职责委托给了某个策略对象。 每个策略对象负责的算法已被各自封装在对象内部。当我们对这些策略对象发出“计算”的消息 时，它们会返回各自不同的计算结果。</p>
<h2 id="封装"><a href="#封装" class="headerlink" title="封装"></a>封装</h2><p>在许多其他语言的对象系统中，封装数据是由语法解析来实现的，这些语言提供了private、public、protected等关键字来提供不同的访问权限；但JS没有提供这些支持，只能用变量作用域实现封装特性。1、使用let来生成块级作用域；2、使用函数作用域的闭包性质；3、通过Symbol来创建私有属性。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> myObject = (<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line">    <span class="keyword">var</span> __name = <span class="string">'sven'</span>; <span class="comment">// 私有（private）变量</span></span><br><span class="line">    <span class="keyword">return</span> &#123; </span><br><span class="line">        getName: <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; <span class="comment">// 公开（public）方法</span></span><br><span class="line">            <span class="keyword">return</span> __name; </span><br><span class="line">        &#125; </span><br><span class="line">    &#125; </span><br><span class="line">&#125;)(); </span><br><span class="line"><span class="built_in">console</span>.log( myObject.getName() ); <span class="comment">// 输出：sven </span></span><br><span class="line"><span class="built_in">console</span>.log( myObject.__name ) <span class="comment">// 输出：undefined</span></span><br></pre></td></tr></table></figure>

<p>上面的封装其实是数据层面的封装，但这只是一种狭义的定义，封装还应该包括：隐藏实现细节、设计细节、隐藏对象的类型。封装使对象之间的耦合变松散，对象之间通过暴露的API接口来通信。当我们修改一个对象时，可以随意地修改它的内部实现，只要对外的接口没有变化，就不会影响到程序的其他功能。</p>
<p>封装实现细节的例子非常之多。拿迭代器来说明，迭代器的作用是在不暴露一个聚合对象的 内部表示的前提下，提供一种方式来顺序访问这个聚合对象。我们编写了一个 each 函数，它的 作用就是遍历一个聚合对象，使用这个 each 函数的人不用关心它的内部是怎样实现的，只要它 提供的功能正确便可以。即使 each 函数修改了内部源代码，只要对外的接口或者调用方式没有 变化，用户就不用关心它内部实现的改变</p>
<p>封装类型是静态类型语言中一种重要的封装方式。一般而言，封装类型是通过抽象类和接口 来进行的。把对象的真正类型隐藏在抽象类或者接口之后，相比对象的类型，客户更关心对象 的行为。在许多静态语言的设计模式中，想方设法地去隐藏对象的类型，也是促使这些模式诞生 的原因之一。比如工厂方法模式、组合模式等。  当然在 JavaScript 中，并没有对抽象类和接口的支持。JavaScript 本身也是一门类型模糊的语 言。在封装类型方面，JavaScript 没有能力，也没有必要做得更多。对于 JavaScript 的设计模式实 现来说，不区分类型是一种失色，也可以说是一种解脱。</p>
<p>拿创建型模式来说，要创建一个对象，是一种抽象行为，而具体创建什么对象则是可以变化 的，创建型模式的目的就是封装创建对象的变化。而结构型模式封装的是对象之间的组合关系。 行为型模式封装的是对象的行为变化。  通过封装变化的方式，把系统中稳定不变的部分和容易变化的部分隔离开来，在系统的演变 过程中，我们只需要替换那些容易变化的部分，如果这些部分是已经封装好的，替换起来也相对容易。这可以最大程度地保证程序的稳定性和可扩展性。</p>
<h2 id="继承"><a href="#继承" class="headerlink" title="继承"></a>继承</h2><p>JS使用的是基于原型链的继承形式。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*11、继承的实现方式与比较</span></span><br><span class="line"><span class="comment">(1)、借助call来实现继承(构造函数继承)</span></span><br><span class="line"><span class="comment">这样写的时候子类虽然能够拿到父类的属性值，但是问题是父类原型对象中一旦存在方法那么子类无法继承。</span></span><br><span class="line"><span class="comment">通过使用call()或apply()方法，Parent构造函数在为Child的实例创建的新对象的上下文执行了，</span></span><br><span class="line"><span class="comment">就相当于新的Child实例对象上运行了Parent()函数中的所有初始化代码，结果就是每个实例都有自己的info属性。</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Parent1</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.name = <span class="string">'parent1'</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">Child1</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">    Parent1.call(<span class="keyword">this</span>);</span><br><span class="line">    <span class="keyword">this</span>.type = <span class="string">'child1'</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="built_in">console</span>.log(<span class="keyword">new</span> Child1);</span><br><span class="line"><span class="comment">/*(2)、原型链实现继承</span></span><br><span class="line"><span class="comment">将父类的实例作为子类的原型：父类的所有引用属性（info）会被所有子类共享，更改一个子类的引用属性，其他子类也会受影响</span></span><br><span class="line"><span class="comment">如下：两个child实例用的是一个原型对象，修改其中之一的play，会影响另一个的。</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Parent2</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.name = <span class="string">'parent2'</span>;</span><br><span class="line">    <span class="keyword">this</span>.play = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>]</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">Child2</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.type = <span class="string">'child2'</span>;</span><br><span class="line">  &#125;</span><br><span class="line">Child2.prototype = <span class="keyword">new</span> Parent2();</span><br><span class="line"><span class="built_in">console</span>.log(<span class="keyword">new</span> Child2());</span><br><span class="line"><span class="comment">/*(3)、组合继承：同时应用上面两种继承方法</span></span><br><span class="line"><span class="comment">使用原型链继承原型上的引用属性和方法，而通过构造函数继承实例属性，这样既可以把方法定义在原型上以实现重用，又可以让每个实例都有自己的属性 */</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Parent3</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.name = <span class="string">'parent3'</span>;</span><br><span class="line">    <span class="keyword">this</span>.play = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>];</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">Child3</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    Parent3.call(<span class="keyword">this</span>);</span><br><span class="line">    <span class="keyword">this</span>.type = <span class="string">'child3'</span>;</span><br><span class="line">  &#125;</span><br><span class="line">  Child3.prototype = <span class="keyword">new</span> Parent3();</span><br><span class="line">  <span class="keyword">var</span> s3 = <span class="keyword">new</span> Child3();</span><br><span class="line">  <span class="keyword">var</span> s4 = <span class="keyword">new</span> Child3();</span><br><span class="line">  s3.play.push(<span class="number">4</span>);</span><br><span class="line">  <span class="built_in">console</span>.log(s3.play, s4.play);</span><br><span class="line"></span><br><span class="line"><span class="comment">/*(4)、寄生组合继承</span></span><br><span class="line"><span class="comment">上面的组合继承有2点需要简单优化，1、避免parent构造函数执行2次；2、子类的构造函数应该指向Child，而不是Parent */</span></span><br><span class="line"><span class="comment">//new创建一个对象，执行构造函数。Object.create相当于创建一个对象，但是不执行构造函数。</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Parent5</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.name = <span class="string">'parent5'</span>;</span><br><span class="line">    <span class="keyword">this</span>.play = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>];</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">Child5</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    Parent5.call(<span class="keyword">this</span>);</span><br><span class="line">    <span class="keyword">this</span>.type = <span class="string">'child5'</span>;</span><br><span class="line">  &#125;</span><br><span class="line">Child5.prototype = <span class="built_in">Object</span>.create(Parent5.prototype);</span><br><span class="line">Child5.prototype.constructor = Child5;</span><br></pre></td></tr></table></figure>

<h2 id="AOP"><a href="#AOP" class="headerlink" title="AOP"></a>AOP</h2><p>AOP：面向切面编程的主要作用是把一些跟核心业务逻辑模块无关的功能抽离出来，这些功能通常包括日志统计、安全控制、异常处理等。把这些功能抽离出来后，再通过“动态织入”的方式掺入业务模块中。这样做的好处是：首先可以保持业务逻辑模块的纯净与高内聚特性，其次是可以方便地复用日志统计等功能模块。</p>
<p>在Java中，可以通过反射、动态代理的机制来实现AOP。而在JavaScript中，AOP的实现更简单，指把一个函数动态织入到另外一个函数之中，通过扩展Function.prototype来实现。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Function</span>.prototype.before = <span class="function"><span class="keyword">function</span>(<span class="params">beforefn</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> _self = <span class="keyword">this</span>; <span class="comment">//保存原函数的引用</span></span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123; <span class="comment">//返回包含原函数和新函数的代理函数</span></span><br><span class="line">        beforefn.apply(<span class="keyword">this</span>, <span class="built_in">arguments</span>);<span class="comment">//执行原函数，修正this</span></span><br><span class="line">        <span class="keyword">return</span> _self.apply(<span class="keyword">this</span>, <span class="built_in">arguments</span>);<span class="comment">//执行原函数</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="built_in">Function</span>.prototype.after = <span class="function"><span class="keyword">function</span>(<span class="params">afterfn</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> _self = <span class="keyword">this</span>;</span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">var</span> ret = _self.apply(<span class="keyword">this</span>, <span class="built_in">arguments</span>);<span class="comment">//先执行原函数,再执行新定义的函数</span></span><br><span class="line">        afterfn.apply(<span class="keyword">this</span>, <span class="built_in">arguments</span>);</span><br><span class="line">        <span class="keyword">return</span> ret;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> func = fucntion()&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="number">2</span>);</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">func = func.before(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="number">1</span>);</span><br><span class="line">&#125;).after(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="built_in">console</span>.log(<span class="number">3</span>);</span><br><span class="line">&#125;)</span><br><span class="line">func();</span><br><span class="line"><span class="comment">//执行该函数，会打印1 2 3。</span></span><br></pre></td></tr></table></figure>

<p>这种使用AOP的方式给函数添加职责，是JS中一种巧妙的装饰器模式实现。</p>
<h2 id="高阶函数"><a href="#高阶函数" class="headerlink" title="高阶函数"></a>高阶函数</h2><h3 id="currying"><a href="#currying" class="headerlink" title="currying"></a>currying</h3><p>柯里化又称部分求职，一个currying的函数首先会接收一些参数，但并不会立即求职，而是继续返回另外一个函数，刚才传入的参数再函数形成的闭包中被保存起来。待到真正需要求值得时候，之前传入得所有参数都会一次性地用于求值。</p>
<p>通用的function currying(){}，其接受一个参数，即要被currying的函数。在该例子中，这个函数的作用是遍历本月每天的开销并求出它们的总和。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> currying = <span class="function"><span class="keyword">function</span>(<span class="params">fn</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> args = [];</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">arguments</span>.length === <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> fn.apply(<span class="keyword">this</span>, args);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            [].push.apply(args, <span class="built_in">arguments</span>);</span><br><span class="line">            <span class="keyword">return</span> <span class="built_in">arguments</span>.callee;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h3 id="uncurrying"><a href="#uncurrying" class="headerlink" title="uncurrying"></a>uncurrying</h3><p>当我们调用对象的某个方法时，其实不用去关心该对象原本是否被设计为拥有该方法，我们通常可以使用call、apply来借用原本不属于它的方法。我们常常让类数组去借用Array.prototype的方法。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line"> <span class="built_in">Array</span>.prototype.push.call( <span class="built_in">arguments</span>, <span class="number">4</span> ); <span class="comment">// arguments 借用 Array.prototype.push 方法</span></span><br><span class="line"> <span class="built_in">console</span>.log( <span class="built_in">arguments</span> ); <span class="comment">// 输出：[1, 2, 3, 4] </span></span><br><span class="line">&#125;)( <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span> );</span><br></pre></td></tr></table></figure>

<p>那么如何把泛化this的过程提取出来？</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">Function</span>.prototype.uncurrying = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123; </span><br><span class="line">    <span class="keyword">var</span> self = <span class="keyword">this</span>; <span class="comment">//self此时是Array.prototype.push</span></span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123; </span><br><span class="line">        <span class="keyword">var</span> obj = <span class="built_in">Array</span>.prototype.shift.call( <span class="built_in">arguments</span> );<span class="comment">//arguments对象的第一个元素被截去</span></span><br><span class="line">        <span class="keyword">return</span> self.apply( obj, <span class="built_in">arguments</span> ); </span><br><span class="line">    &#125;; </span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">在类数组对象 arguments 借用 Array.prototype 的方法之前，</span></span><br><span class="line"><span class="comment">先把 Array.prototype.push.call这句代码转换为一个通用的 push 函数：</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">var</span> push = <span class="built_in">Array</span>.prototype.push.uncurrying(); </span><br><span class="line">(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line">    push( <span class="built_in">arguments</span>, <span class="number">4</span> ); </span><br><span class="line">    <span class="built_in">console</span>.log( <span class="built_in">arguments</span> ); <span class="comment">// 输出：[1, 2, 3, 4] </span></span><br><span class="line">&#125;)( <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span> ); </span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">通过 uncurrying 的方式，Array.prototype.push.call 变成了一个通用的 push 函数。这样一来，</span></span><br><span class="line"><span class="comment">push 函数的作用就跟 Array.prototype.push 一样了，同样不仅仅局限于只能操作 array 对象。而</span></span><br><span class="line"><span class="comment">对于使用者而言，调用 push 函数的方式也显得更加简洁和意图明了。</span></span><br><span class="line"><span class="comment">我们还可以一次性地把 Array.prototype 上的方法“复制”到 array 对象上，同样这些方法可</span></span><br><span class="line"><span class="comment">操作的对象也不仅仅只是 array 对象：</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">for</span> ( <span class="keyword">var</span> i = <span class="number">0</span>, fn, ary = [ <span class="string">'push'</span>, <span class="string">'shift'</span>, <span class="string">'forEach'</span> ]; fn = ary[ i++ ]; )&#123; </span><br><span class="line">    <span class="built_in">Array</span>[ fn ] = <span class="built_in">Array</span>.prototype[ fn ].uncurrying(); </span><br><span class="line">&#125;; </span><br><span class="line"><span class="keyword">var</span> obj = &#123; </span><br><span class="line">    <span class="string">"length"</span>: <span class="number">3</span>, </span><br><span class="line">    <span class="string">"0"</span>: <span class="number">1</span>, </span><br><span class="line">    <span class="string">"1"</span>: <span class="number">2</span>, </span><br><span class="line">    <span class="string">"2"</span>: <span class="number">3</span> </span><br><span class="line">&#125;; </span><br><span class="line"><span class="built_in">Array</span>.push( obj, <span class="number">4</span> ); <span class="comment">// 向对象中添加一个元素</span></span><br><span class="line"><span class="built_in">console</span>.log( obj.length ); <span class="comment">// 输出：4 </span></span><br><span class="line"><span class="keyword">var</span> first = <span class="built_in">Array</span>.shift( obj ); <span class="comment">// 截取第一个元素</span></span><br><span class="line"><span class="built_in">console</span>.log( first ); <span class="comment">// 输出：1 </span></span><br><span class="line"><span class="built_in">console</span>.log( obj ); <span class="comment">// 输出：&#123;0: 2, 1: 3, 2: 4, length: 3&#125; </span></span><br><span class="line"></span><br><span class="line"><span class="built_in">Array</span>.forEach( obj, <span class="function"><span class="keyword">function</span>(<span class="params"> i, n </span>)</span>&#123; </span><br><span class="line">    <span class="built_in">console</span>.log( n ); <span class="comment">// 分别输出：0, 1, 2 </span></span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure>

<h3 id="分时函数"><a href="#分时函数" class="headerlink" title="分时函数"></a>分时函数</h3><p>在短时间内往页面中大量添加DOM节点显然会让浏览器吃不消，往往会导致浏览器的卡顿或者假死。这个问题的解决方法之一是下面的timeChunk函数，timeChunk函数让创建节点的工作分批进行，比如：1秒钟创建1000个节点，改为每隔200ms创建8个节点。该函数接收3个参数，第1个参数是创建节点时需要用到的数据，第2个参数是封装了创建节点逻辑的函数，第3个参数表示每一批创建的节点数量。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> timeChunk = <span class="function"><span class="keyword">function</span> (<span class="params">ary, fn, count</span>) </span>&#123;</span><br><span class="line">    <span class="keyword">var</span> obj;<span class="keyword">var</span> t;</span><br><span class="line">    <span class="keyword">var</span> len = ary.length;</span><br><span class="line">    <span class="keyword">var</span> start = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; <span class="built_in">Math</span>.min(count || <span class="number">1</span>, ary.length); i++) &#123;</span><br><span class="line">            <span class="keyword">var</span> obj = ary.shift();</span><br><span class="line">            fn(obj);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">        t = setInterval(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">            <span class="keyword">if</span> (ary.length === <span class="number">0</span>) &#123;<span class="comment">//如果全部节点都已经被创建好</span></span><br><span class="line">                <span class="keyword">return</span> clearInterval(t);</span><br><span class="line">            &#125;</span><br><span class="line">            start();</span><br><span class="line">        &#125;, <span class="number">200</span>); <span class="comment">//分批执行的时间间隔，也可以用参数的形式传入</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h1 id="单例模式"><a href="#单例模式" class="headerlink" title="单例模式"></a>单例模式</h1><p>要实现一个标准的单例模式并不复杂，无非是用一个变量来标志当前是否已经为某个类创建 过对象，如果是，则在下一次获取该类的实例时，直接返回之前创建的对象。我们通过 Singleton.getInstance 来获取 Singleton 类的唯一对象，这种方式相对简单，但有 一个问题，就是增加了这个类的“不透明性”，Singleton 类的使用者必须知道这是一个单例类， 跟以往通过 new XXX 的方式来获取对象不同，这里偏要使用 Singleton.getInstance 来获取对象。</p>
<h2 id="透明单例"><a href="#透明单例" class="headerlink" title="透明单例"></a>透明单例</h2><p>我们现在的目标是实现一个“透明”的单例类，用户从这个类中创建对象的时候，可以像使 用其他任何普通类一样。在下面的例子中，我们将使用 CreateDiv 单例类，它的作用是负责在页 面中创建唯一的 div 节点，代码如下：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> CreateDiv = (<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line">    <span class="keyword">var</span> instance; </span><br><span class="line">    <span class="keyword">var</span> CreateDiv = <span class="function"><span class="keyword">function</span>(<span class="params"> html </span>)</span>&#123; </span><br><span class="line">        <span class="keyword">if</span> ( instance )&#123; </span><br><span class="line">            <span class="keyword">return</span> instance; </span><br><span class="line">        &#125; </span><br><span class="line">        <span class="keyword">this</span>.html = html; </span><br><span class="line">        <span class="keyword">this</span>.init(); </span><br><span class="line">        <span class="keyword">return</span> instance = <span class="keyword">this</span>; </span><br><span class="line">    &#125;; </span><br><span class="line">    CreateDiv.prototype.init = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line">        <span class="keyword">var</span> div = <span class="built_in">document</span>.createElement( <span class="string">'div'</span> ); </span><br><span class="line">        div.innerHTML = <span class="keyword">this</span>.html; </span><br><span class="line">        <span class="built_in">document</span>.body.appendChild( div ); </span><br><span class="line">    &#125;; </span><br><span class="line">    <span class="keyword">return</span> CreateDiv; </span><br><span class="line">&#125;)(); </span><br><span class="line"><span class="keyword">var</span> a = <span class="keyword">new</span> CreateDiv( <span class="string">'sven1'</span> ); </span><br><span class="line"><span class="keyword">var</span> b = <span class="keyword">new</span> CreateDiv( <span class="string">'sven2'</span> ); </span><br><span class="line">alert ( a === b ); <span class="comment">// true</span></span><br></pre></td></tr></table></figure>

<p>虽然现在完成了一个透明的单例类的编写，但它同样有一些缺点。 为了把 instance 封装起来，我们使用了自执行的匿名函数和闭包，并且让这个匿名函数返回 真正的 Singleton 构造方法，这增加了一些程序的复杂度，阅读起来也不是很舒服</p>
<p>CreateDiv 的构造函数实际上负责了两件事情。第一是创建对象和执行初始化init 方法，第二是保证只有一个对象。虽然我们目前还没有接触过“单一职责原则”的概念， 但可以明确的是，这是一种不好的做法，至少这个构造函数看起来很奇怪。</p>
<h2 id="代理实现"><a href="#代理实现" class="headerlink" title="代理实现"></a>代理实现</h2><p>现在我们通过引入代理类的方式，来解决上面提到的问题。 我们依然使用上面的代码，首先在 CreateDiv 构造函数中，把负责管理单例的代码移除 出去，使它成为一个普通的创建 div 的类。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> CreateDiv = <span class="function"><span class="keyword">function</span>(<span class="params"> html </span>)</span>&#123; </span><br><span class="line">    <span class="keyword">this</span>.html = html;</span><br><span class="line">    <span class="keyword">this</span>.init(); </span><br><span class="line">&#125;; </span><br><span class="line">CreateDiv.prototype.init = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line">    <span class="keyword">var</span> div = <span class="built_in">document</span>.createElement( <span class="string">'div'</span> ); </span><br><span class="line">    div.innerHTML = <span class="keyword">this</span>.html; </span><br><span class="line">    <span class="built_in">document</span>.body.appendChild( div ); </span><br><span class="line">&#125;; </span><br><span class="line"></span><br><span class="line"><span class="comment">//接下来引入代理类 proxySingletonCreateDiv</span></span><br><span class="line"><span class="keyword">var</span> ProxySingletonCreateDiv = (<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line">    <span class="keyword">var</span> instance; </span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params"> html </span>)</span>&#123; </span><br><span class="line">        <span class="keyword">if</span> ( !instance )&#123; </span><br><span class="line">            instance = <span class="keyword">new</span> CreateDiv( html ); </span><br><span class="line">        &#125; </span><br><span class="line">        <span class="keyword">return</span> instance; </span><br><span class="line">    &#125;</span><br><span class="line">&#125;)(); </span><br><span class="line"><span class="keyword">var</span> a = <span class="keyword">new</span> ProxySingletonCreateDiv( <span class="string">'sven1'</span> ); </span><br><span class="line"><span class="keyword">var</span> b = <span class="keyword">new</span> ProxySingletonCreateDiv( <span class="string">'sven2'</span> ); </span><br><span class="line">alert ( a === b );</span><br></pre></td></tr></table></figure>

<p>通过引入代理类的方式，我们同样完成了一个单例模式的编写，跟之前不同的是，现在我们 把负责管理单例的逻辑移到了代理类 proxySingletonCreateDiv 中。这样一来，CreateDiv 就变成了 一个普通的类，它跟 proxySingletonCreateDiv 组合起来可以达到单例模式的效果。 本例是缓存代理的应用之一。</p>
<h2 id="惰性单例"><a href="#惰性单例" class="headerlink" title="惰性单例"></a>惰性单例</h2><p>惰性单例指的是在需要的时候才创建对象实例。惰性单例是单例模式的重点，这种技术在实 际开发中非常有用，有用的程度可能超出了我们的想象，实际上在本章开头就使用过这种技术， instance 实例对象总是在我们调用 Singleton.getInstance 的时候才被创建，而不是在页面加载好 的时候就创建。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> getSingle = <span class="function"><span class="keyword">function</span>(<span class="params"> fn </span>)</span>&#123; </span><br><span class="line">    <span class="keyword">var</span> result; </span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line">        <span class="keyword">return</span> result || ( result = fn .apply(<span class="keyword">this</span>, <span class="built_in">arguments</span> ) ); </span><br><span class="line">    &#125; </span><br><span class="line">&#125;; </span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> createLoginLayer = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line">    <span class="keyword">var</span> div = <span class="built_in">document</span>.createElement( <span class="string">'div'</span> ); </span><br><span class="line">    div.innerHTML = <span class="string">'我是登录浮窗'</span>; </span><br><span class="line">    div.style.display = <span class="string">'none'</span>; </span><br><span class="line">    <span class="built_in">document</span>.body.appendChild( div ); </span><br><span class="line">    <span class="keyword">return</span> div; </span><br><span class="line">&#125;; </span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> createSingleLoginLayer = getSingle( createLoginLayer );</span><br><span class="line"><span class="built_in">document</span>.getElementById( <span class="string">'loginBtn'</span> ).onclick = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line">    <span class="keyword">var</span> loginLayer = createSingleLoginLayer(); </span><br><span class="line">    loginLayer.style.display = <span class="string">'block'</span>; </span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>在这个例子中，我们把创建实例对象的职责和管理单例的职责分别放置在两个方法里，这两 个方法可以独立变化而互不影响，当它们连接在一起的时候，就完成了创建唯一实例对象的功能， 看起来是一件挺奇妙的事情。</p>
<h1 id="策略模式"><a href="#策略模式" class="headerlink" title="策略模式"></a>策略模式</h1><p><strong>策略模式的定义是：定义一系列的算法，把它们一个个封装起来，并且使它们可以相互替换。</strong></p>
<p>策略模式指的是定义一系 列的算法，把它们一个个封装起来。将不变的部分和变化的部分隔开是每个设计模式的主题，策 略模式也不例外，策略模式的目的就是将算法的使用与算法的实现分离开来。</p>
<p>一个基于策略模式的程序至少由两部分组成。第一个部分是一组策略类，策略类封装了具体 的算法，并负责具体的计算过程。第二个部分是环境类 Context，Context 接受客户的请求，随后 把请求委托给某一个策略类。要做到这点，说明 Context 中要维持对某个策略对象的引用。</p>
<h2 id="简单实现"><a href="#简单实现" class="headerlink" title="简单实现"></a>简单实现</h2><p>第一个版本是模仿传统面向对象语言中的实现。我们先 把每种绩效的计算规则都封装在对应的策略类里面：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> performanceS = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;&#125;; </span><br><span class="line">performanceS.prototype.calculate = <span class="function"><span class="keyword">function</span>(<span class="params"> salary </span>)</span>&#123; </span><br><span class="line">    <span class="keyword">return</span> salary * <span class="number">4</span>; </span><br><span class="line">&#125;; </span><br><span class="line"><span class="keyword">var</span> performanceA = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;&#125;; </span><br><span class="line">performanceA.prototype.calculate = <span class="function"><span class="keyword">function</span>(<span class="params"> salary </span>)</span>&#123; </span><br><span class="line">    <span class="keyword">return</span> salary * <span class="number">3</span>; </span><br><span class="line">&#125;; </span><br><span class="line"><span class="keyword">var</span> performanceB = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;&#125;; </span><br><span class="line">performanceB.prototype.calculate = <span class="function"><span class="keyword">function</span>(<span class="params"> salary </span>)</span>&#123; </span><br><span class="line">    <span class="keyword">return</span> salary * <span class="number">2</span>; </span><br><span class="line">&#125;; </span><br><span class="line"></span><br><span class="line"><span class="comment">//接下来定义奖金类 Bonus：</span></span><br><span class="line"><span class="keyword">var</span> Bonus = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line">    <span class="keyword">this</span>.salary = <span class="literal">null</span>; <span class="comment">// 原始工资</span></span><br><span class="line">    <span class="keyword">this</span>.strategy = <span class="literal">null</span>; <span class="comment">// 绩效等级对应的策略对象</span></span><br><span class="line">&#125;; </span><br><span class="line">Bonus.prototype.setSalary = <span class="function"><span class="keyword">function</span>(<span class="params"> salary </span>)</span>&#123; </span><br><span class="line">    <span class="keyword">this</span>.salary = salary; <span class="comment">// 设置员工的原始工资</span></span><br><span class="line">&#125;; </span><br><span class="line">Bonus.prototype.setStrategy = <span class="function"><span class="keyword">function</span>(<span class="params"> strategy </span>)</span>&#123; </span><br><span class="line">    <span class="keyword">this</span>.strategy = strategy; <span class="comment">// 设置员工绩效等级对应的策略对象</span></span><br><span class="line">&#125;; </span><br><span class="line">Bonus.prototype.getBonus = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; <span class="comment">// 取得奖金数额</span></span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">this</span>.strategy.calculate( <span class="keyword">this</span>.salary ); <span class="comment">// 把计算奖金的操作委托给对应的策略对象</span></span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">定义一系列的算法，把它们各自封装成策略类，算法被</span></span><br><span class="line"><span class="comment">封装在策略类内部的方法里。在客户对 Context 发起请求的时候，Context 总是把请求委托给这些</span></span><br><span class="line"><span class="comment">策略对象中间的某一个进行计算</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">var</span> bonus = <span class="keyword">new</span> Bonus(); </span><br><span class="line">bonus.setSalary( <span class="number">10000</span> ); </span><br><span class="line">bonus.setStrategy( <span class="keyword">new</span> performanceS() ); <span class="comment">// 设置策略对象</span></span><br><span class="line"><span class="built_in">console</span>.log( bonus.getBonus() ); <span class="comment">// 输出：40000 </span></span><br><span class="line">bonus.setStrategy( <span class="keyword">new</span> performanceA() ); <span class="comment">// 设置策略对象</span></span><br><span class="line"><span class="built_in">console</span>.log( bonus.getBonus() ); <span class="comment">// 输出：30000</span></span><br></pre></td></tr></table></figure>

<p>我们让 strategy 对象从各个策略类中创建而来，这是模拟一些传统面向对象语言的实现。实际上在 JavaScript 语言中，函数也是对象，所以更简单和直接的做法是把 strategy 直接定义为函数：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> strategies = &#123; </span><br><span class="line">    <span class="string">"S"</span>: <span class="function"><span class="keyword">function</span>(<span class="params"> salary </span>)</span>&#123; </span><br><span class="line">        <span class="keyword">return</span> salary * <span class="number">4</span>; </span><br><span class="line">    &#125;, </span><br><span class="line">    <span class="string">"A"</span>: <span class="function"><span class="keyword">function</span>(<span class="params"> salary </span>)</span>&#123; </span><br><span class="line">        <span class="keyword">return</span> salary * <span class="number">3</span>; </span><br><span class="line">    &#125;, </span><br><span class="line">    <span class="string">"B"</span>: <span class="function"><span class="keyword">function</span>(<span class="params"> salary </span>)</span>&#123; </span><br><span class="line">        <span class="keyword">return</span> salary * <span class="number">2</span>; </span><br><span class="line">    &#125; </span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">//同样，Context 也没有必要必须用 Bonus 类来表示，我们依然用 calculateBonus 函数充当Context 来接受用户的请求。</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> calculateBonus = <span class="function"><span class="keyword">function</span>(<span class="params"> level, salary </span>)</span>&#123; </span><br><span class="line">    <span class="keyword">return</span> strategies[ level ]( salary ); </span><br><span class="line">&#125;; </span><br><span class="line"><span class="built_in">console</span>.log(calculateBonus( <span class="string">'S'</span>, <span class="number">20000</span> ) ); <span class="comment">// 输出：80000 </span></span><br><span class="line"><span class="built_in">console</span>.log(calculateBonus( <span class="string">'A'</span>, <span class="number">10000</span> ) ); <span class="comment">// 输出：30000 </span></span><br><span class="line"><span class="comment">//在接下来的缓动动画和表单验证的例子中，我们用到的都是这种函数形式的策略对象。</span></span><br></pre></td></tr></table></figure>

<p>多态：通过使用策略模式重构代码，我们消除了原程序中大片的条件分支语句。所有跟计算奖金有 关的逻辑不再放在 Context 中，而是分布在各个策略对象中。Context 并没有计算奖金的能力，而 是把这个职责委托给了某个策略对象。每个策略对象负责的算法已被各自封装在对象内部。当我 们对这些策略对象发出“计算奖金”的请求时，它们会返回各自不同的计算结果，这正是对象多 态性的体现，也是“它们可以相互替换”的目的。</p>
<h2 id="表单验证"><a href="#表单验证" class="headerlink" title="表单验证"></a>表单验证</h2><p>下面我们将用策略模式来重构表单校验的代码，很显然第一步我们要把这些校验逻辑都封装成策略对象：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> strategies = &#123; </span><br><span class="line">    isNonEmpty: <span class="function"><span class="keyword">function</span>(<span class="params"> value, errorMsg </span>)</span>&#123; <span class="comment">// 不为空</span></span><br><span class="line">        <span class="keyword">if</span> ( value === <span class="string">''</span> )&#123; </span><br><span class="line">            <span class="keyword">return</span> errorMsg ; </span><br><span class="line">        &#125; </span><br><span class="line">    &#125;, </span><br><span class="line">    minLength: <span class="function"><span class="keyword">function</span>(<span class="params"> value, length, errorMsg </span>)</span>&#123; <span class="comment">// 限制最小长度</span></span><br><span class="line">        <span class="keyword">if</span> ( value.length &lt; length )&#123; </span><br><span class="line">            <span class="keyword">return</span> errorMsg;</span><br><span class="line">        &#125; </span><br><span class="line">    &#125;, </span><br><span class="line">    isMobile: <span class="function"><span class="keyword">function</span>(<span class="params"> value, errorMsg </span>)</span>&#123; <span class="comment">// 手机号码格式</span></span><br><span class="line">        <span class="keyword">if</span> ( !<span class="regexp">/(^1[3|5|8][0-9]&#123;9&#125;$)/</span>.test( value ) )&#123; </span><br><span class="line">            <span class="keyword">return</span> errorMsg; </span><br><span class="line">        &#125; </span><br><span class="line">    &#125; </span><br><span class="line">&#125;; </span><br><span class="line"></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">接下来我们准备实现 Validator类。Validator 类在这里作为 Context，负责接收用户的请求</span></span><br><span class="line"><span class="comment">并委托给 strategy 对象。在给出 Validator 类的代码之前，有必要提前了解用户是如何向 Validator</span></span><br><span class="line"><span class="comment">类发送请求的，这有助于我们知道如何去编写 Validator 类的代码。</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">var</span> validataFunc = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line">    <span class="keyword">var</span> validator = <span class="keyword">new</span> Validator(); <span class="comment">// 创建一个 validator 对象</span></span><br><span class="line"> <span class="comment">/***************添加一些校验规则****************/</span> </span><br><span class="line">    validator.add( registerForm.userName, <span class="string">'isNonEmpty'</span>, <span class="string">'用户名不能为空'</span> ); </span><br><span class="line">    validator.add( registerForm.password, <span class="string">'minLength:6'</span>, <span class="string">'密码长度不能少于 6 位'</span> ); </span><br><span class="line">    validator.add( registerForm.phoneNumber, <span class="string">'isMobile'</span>, <span class="string">'手机号码格式不正确'</span> ); </span><br><span class="line">    <span class="keyword">var</span> errorMsg = validator.start(); <span class="comment">// 获得校验结果</span></span><br><span class="line">    <span class="keyword">return</span> errorMsg; <span class="comment">// 返回校验结果</span></span><br><span class="line">&#125; </span><br><span class="line"><span class="keyword">var</span> registerForm = <span class="built_in">document</span>.getElementById( <span class="string">'registerForm'</span> ); </span><br><span class="line">registerForm.onsubmit = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line">    <span class="keyword">var</span> errorMsg = validataFunc(); <span class="comment">// 如果 errorMsg 有确切的返回值，说明未通过校验</span></span><br><span class="line">    <span class="keyword">if</span> ( errorMsg )&#123; </span><br><span class="line">        alert ( errorMsg );</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>; <span class="comment">// 阻止表单提交</span></span><br><span class="line">    &#125; </span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">//最后是Validator类的实现。</span></span><br><span class="line"><span class="keyword">var</span> Validator = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line">    <span class="keyword">this</span>.cache = []; <span class="comment">// 保存校验规则</span></span><br><span class="line">&#125;; </span><br><span class="line">Validator.prototype.add = <span class="function"><span class="keyword">function</span>(<span class="params"> dom, rule, errorMsg </span>)</span>&#123; </span><br><span class="line">    <span class="keyword">var</span> ary = rule.split( <span class="string">':'</span> ); <span class="comment">// 把 strategy 和参数分开，在有些策略里面":"后面是参数。</span></span><br><span class="line">    <span class="keyword">this</span>.cache.push(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; <span class="comment">// 把校验的步骤用空函数包装起来，并且放入 cache </span></span><br><span class="line">        <span class="keyword">var</span> strategy = ary.shift(); <span class="comment">// 用户挑选的 strategy </span></span><br><span class="line">        ary.unshift( dom.value ); <span class="comment">// 把 input 的 value 添加进参数列表</span></span><br><span class="line">        ary.push( errorMsg ); <span class="comment">// 把 errorMsg 添加进参数列表</span></span><br><span class="line">        <span class="keyword">return</span> strategies[ strategy ].apply( dom, ary ); </span><br><span class="line">    &#125;); </span><br><span class="line">&#125;;</span><br><span class="line">Validator.prototype.start = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line">    <span class="keyword">for</span> ( <span class="keyword">var</span> i = <span class="number">0</span>, validatorFunc; validatorFunc = <span class="keyword">this</span>.cache[ i++ ]; )&#123; </span><br><span class="line">        <span class="keyword">var</span> msg = validatorFunc(); <span class="comment">// 开始校验，并取得校验后的返回信息</span></span><br><span class="line">        <span class="keyword">if</span> ( msg )&#123; <span class="comment">// 如果有确切的返回值，说明校验没有通过</span></span><br><span class="line">            <span class="keyword">return</span> msg; </span><br><span class="line">        &#125; </span><br><span class="line">    &#125; </span><br><span class="line">&#125;; </span><br><span class="line"></span><br><span class="line"><span class="comment">/*使用策略模式重构代码后，我们仅仅通过“配置”的方式就可以完成一个表单的验证，</span></span><br><span class="line"><span class="comment">这些校验规则也可以复用在程序的任何地方，啊还能作为插件的形式，方便地被移植到其他项目中。</span></span><br><span class="line"><span class="comment">在修改某个校验规则时，只需要编写或改写少量的代码。</span></span><br><span class="line"><span class="comment">比如我们想将用户名输入框的校验规则改成用户名不能少于 4 个字符。可以看到，这时候的修改是毫不费力的。</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line">validator.add( registerForm.userName, <span class="string">'isNonEmpty'</span>, <span class="string">'用户名不能为空'</span> ); </span><br><span class="line"><span class="comment">// 改成：</span></span><br><span class="line">validator.add( registerForm.userName, <span class="string">'minLength:10'</span>, <span class="string">'用户名长度不能小于 10 位'</span> );</span><br></pre></td></tr></table></figure>

<p>为了专注于策略模式的使用，我们目前的验证实现留有一部分的缺陷：一个文本框只能对应一种校验规则，比如，想使用以下代码的形式来做校验：</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br></pre></td><td class="code"><pre><span class="line">validator.add( registerForm.userName, [&#123; </span><br><span class="line"> strategy: <span class="string">'isNonEmpty'</span>, </span><br><span class="line"> errorMsg: <span class="string">'用户名不能为空'</span> </span><br><span class="line">&#125;, &#123; </span><br><span class="line"> strategy: <span class="string">'minLength:6'</span>, </span><br><span class="line"> errorMsg: <span class="string">'用户名长度不能小于 10 位'</span> </span><br><span class="line">&#125;]); </span><br><span class="line"></span><br><span class="line"><span class="comment">//下面提供的代码可用于一个文本输入框对应多种校验规则：</span></span><br><span class="line"><span class="keyword">var</span> strategies = &#123; </span><br><span class="line">    isNonEmpty: <span class="function"><span class="keyword">function</span>(<span class="params"> value, errorMsg </span>)</span>&#123; </span><br><span class="line">        <span class="keyword">if</span> ( value === <span class="string">''</span> )&#123; </span><br><span class="line">            <span class="keyword">return</span> errorMsg; </span><br><span class="line">        &#125; </span><br><span class="line">    &#125;, </span><br><span class="line">    minLength: <span class="function"><span class="keyword">function</span>(<span class="params"> value, length, errorMsg </span>)</span>&#123; </span><br><span class="line">        <span class="keyword">if</span> ( value.length &lt; length )&#123; </span><br><span class="line">            <span class="keyword">return</span> errorMsg; </span><br><span class="line">        &#125; </span><br><span class="line">    &#125;, </span><br><span class="line">    isMobile: <span class="function"><span class="keyword">function</span>(<span class="params"> value, errorMsg </span>)</span>&#123; </span><br><span class="line">        <span class="keyword">if</span> ( !<span class="regexp">/(^1[3|5|8][0-9]&#123;9&#125;$)/</span>.test( value ) )&#123; </span><br><span class="line">            <span class="keyword">return</span> errorMsg; </span><br><span class="line">        &#125; </span><br><span class="line">    &#125; </span><br><span class="line">&#125;; </span><br><span class="line"> <span class="comment">/***********************Validator 类**************************/</span> </span><br><span class="line"><span class="keyword">var</span> Validator = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line">    <span class="keyword">this</span>.cache = []; </span><br><span class="line">&#125;; </span><br><span class="line">Validator.prototype.add = <span class="function"><span class="keyword">function</span>(<span class="params"> dom, rules </span>)</span>&#123; </span><br><span class="line">    <span class="keyword">var</span> self = <span class="keyword">this</span>; </span><br><span class="line">    <span class="keyword">for</span> ( <span class="keyword">var</span> i = <span class="number">0</span>, rule; rule = rules[ i++ ]; ) &#123; </span><br><span class="line">        (<span class="function"><span class="keyword">function</span>(<span class="params"> rule </span>)</span>&#123; </span><br><span class="line">            <span class="keyword">var</span> strategyAry = rule.strategy.split( <span class="string">':'</span> ); </span><br><span class="line">            <span class="keyword">var</span> errorMsg = rule.errorMsg; </span><br><span class="line">            self.cache.push(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line">                <span class="keyword">var</span> strategy = strategyAry.shift();</span><br><span class="line">                strategyAry.unshift( dom.value ); </span><br><span class="line">                strategyAry.push( errorMsg ); </span><br><span class="line">                <span class="keyword">return</span> strategies[ strategy ].apply( dom, strategyAry ); </span><br><span class="line">            &#125;); </span><br><span class="line">        &#125;)( rule ) </span><br><span class="line">    &#125; </span><br><span class="line"> &#125;; </span><br><span class="line"></span><br><span class="line">Validator.prototype.start = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line">    <span class="keyword">for</span> ( <span class="keyword">var</span> i = <span class="number">0</span>, validatorFunc; validatorFunc = <span class="keyword">this</span>.cache[ i++ ]; )&#123; </span><br><span class="line">        <span class="keyword">var</span> errorMsg = validatorFunc(); </span><br><span class="line">        <span class="keyword">if</span> ( errorMsg )&#123; </span><br><span class="line">            <span class="keyword">return</span> errorMsg; </span><br><span class="line">        &#125; </span><br><span class="line">    &#125; </span><br><span class="line">&#125;; </span><br><span class="line"></span><br><span class="line"><span class="comment">/***********************客户调用代码**************************/</span> </span><br><span class="line"><span class="keyword">var</span> registerForm = <span class="built_in">document</span>.getElementById( <span class="string">'registerForm'</span> ); </span><br><span class="line"><span class="keyword">var</span> validataFunc = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line">    <span class="keyword">var</span> validator = <span class="keyword">new</span> Validator(); </span><br><span class="line">    validator.add( registerForm.userName, [&#123; </span><br><span class="line">        strategy: <span class="string">'isNonEmpty'</span>, </span><br><span class="line">        errorMsg: <span class="string">'用户名不能为空'</span> </span><br><span class="line">    &#125;, &#123; </span><br><span class="line">        strategy: <span class="string">'minLength:6'</span>, </span><br><span class="line">        errorMsg: <span class="string">'用户名长度不能小于 10 位'</span> </span><br><span class="line">    &#125;]); </span><br><span class="line">    validator.add( registerForm.password, [&#123; </span><br><span class="line">        strategy: <span class="string">'minLength:6'</span>, </span><br><span class="line">        errorMsg: <span class="string">'密码长度不能小于 6 位'</span> </span><br><span class="line">    &#125;]); </span><br><span class="line">    validator.add( registerForm.phoneNumber, [&#123; </span><br><span class="line">        strategy: <span class="string">'isMobile'</span>, </span><br><span class="line">        errorMsg: <span class="string">'手机号码格式不正确'</span> </span><br><span class="line">    &#125;]); </span><br><span class="line">    <span class="keyword">var</span> errorMsg = validator.start(); </span><br><span class="line">    <span class="keyword">return</span> errorMsg; </span><br><span class="line">&#125; </span><br><span class="line">registerForm.onsubmit = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line">    <span class="keyword">var</span> errorMsg = validataFunc(); </span><br><span class="line">    <span class="keyword">if</span> ( errorMsg )&#123; </span><br><span class="line">        alert ( errorMsg ); </span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>; </span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>策略模式的优点：1、策略模式利用组合、委托和多态等技术和思想，可以有效地避免多重条件选择语句。 2、策略模式提供了对开放—封闭原则的完美支持，将算法封装在独立的 strategy 中，使得它们易于切换，易于理解，易于扩展。 3、 策略模式中的算法也可以复用在系统的其他地方，从而避免许多重复的复制粘贴工作。4、在策略模式中利用组合和委托来让 Context 拥有执行算法的能力，这也是继承的一种更轻便的替代方案。</p>
<h2 id="优缺点"><a href="#优缺点" class="headerlink" title="优缺点"></a>优缺点</h2><p>缺点：1、使用策略模式会在程序中增加许多策略类或者策略对象，但实际上这比把它们负责的 逻辑堆砌在 Context 中要好。2、要使用策略模式，必须了解所有的 strategy，必须了解各个 strategy 之间的不同点， 这样才能选择一个合适的 strategy。</p>
<p>“在函数作为一等对象的语言中，策略模式是隐形的。 strategy 就是值为函数的变量。”在 JavaScript 中，除了使用类来封装算法和行为之外，使用函数 当然也是一种选择。这些“算法”可以被封装到函数中并且四处传递，也就是我们常说的“高阶 函数”。实际上在 JavaScript 这种将函数作为一等对象的语言里，策略模式已经融入到了语言本身 当中，我们经常用高阶函数来封装不同的行为，并且把它传递到另一个函数中。当我们对这些函 数发出“调用”的消息时，不同的函数会返回不同的执行结果。</p>
<p>在 JavaScript 语言的策略模式中，策略类往往被函数所代替，这时策略模式就 成为一种“隐形”的模式。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> S = <span class="function"><span class="keyword">function</span>(<span class="params"> salary </span>)</span>&#123; </span><br><span class="line">    <span class="keyword">return</span> salary * <span class="number">4</span>; </span><br><span class="line">&#125;; </span><br><span class="line"><span class="keyword">var</span> A = <span class="function"><span class="keyword">function</span>(<span class="params"> salary </span>)</span>&#123; </span><br><span class="line">    <span class="keyword">return</span> salary * <span class="number">3</span>; </span><br><span class="line">&#125;; </span><br><span class="line"><span class="keyword">var</span> B = <span class="function"><span class="keyword">function</span>(<span class="params"> salary </span>)</span>&#123; </span><br><span class="line">    <span class="keyword">return</span> salary * <span class="number">2</span>; </span><br><span class="line">&#125;; </span><br><span class="line"><span class="keyword">var</span> calculateBonus = <span class="function"><span class="keyword">function</span>(<span class="params"> func, salary </span>)</span>&#123; </span><br><span class="line">    <span class="keyword">return</span> func( salary ); </span><br><span class="line">&#125;; </span><br><span class="line">calculateBonus( S, <span class="number">10000</span> ); <span class="comment">// 输出：40000</span></span><br></pre></td></tr></table></figure>

<h1 id="代理模式"><a href="#代理模式" class="headerlink" title="代理模式"></a>代理模式</h1><p>代理模式是为一个对象提供一个代用品或占位符，以便控制对它的访问。代理模式的关键是，当客户不方便直接访问一个对象或者不满足需要的时候，提供一个替身 对象来控制对这个对象的访问，客户实际上访问的是替身对象。替身对象对请求做出一些处理之 后，再把请求转交给本体对象。</p>
<h2 id="代理分类"><a href="#代理分类" class="headerlink" title="代理分类"></a>代理分类</h2><p>代理 B 可以帮助 A 过滤掉一些请求，比如送花的人中年龄太大的或者没有宝马的，这种请求就可以直接在代理 B 处被拒绝掉。这种代理叫作<strong>保护代理</strong>。A 和 B 一个充当白脸，一个充当黑脸。白脸 A 继续保持 良好的女神形象，不希望直接拒绝任何人，于是找了黑脸 B 来控制对 A 的访问。 另外，假设现实中的花价格不菲，导致在程序世界里，new Flower 也是一个代价昂贵的操作， 那么我们可以把 new Flower 的操作交给代理 B 去执行，代理 B 会选择在 A 心情好时再执行 new  Flower，这是代理模式的另一种形式，叫作<strong>虚拟代理</strong>。</p>
<p>保护代理用于控制不同权限的对象对目标对象的访问，但在 JavaScript 并不容易实现保护代 理，因为我们无法判断谁访问了某个对象。而虚拟代理是最常用的一种代理模式。</p>
<h3 id="图片预加载"><a href="#图片预加载" class="headerlink" title="图片预加载"></a>图片预加载</h3><p>在 Web 开发中，图片预加载是一种常用的技术，如果直接给某个 img 标签节点设置 src 属性， 由于图片过大或者网络不佳，图片的位置往往有段时间会是一片空白。常见的做法是先用一张 loading 图片占位，然后用异步的方式加载图片，等图片加载好了再把它填充到 img 节点里，这种 场景就很适合使用虚拟代理。 </p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*现在开始引入代理对象 proxyImage，通过这个代理对象，在图片被真正加载好之前，页面中</span></span><br><span class="line"><span class="comment">将出现一张占位的菊花图 loading.gif, 来提示用户图片正在加载。代码如下：*/</span></span><br><span class="line"><span class="keyword">var</span> myImage = (<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line">    <span class="keyword">var</span> imgNode = <span class="built_in">document</span>.createElement( <span class="string">'img'</span> ); </span><br><span class="line">    <span class="built_in">document</span>.body.appendChild( imgNode ); </span><br><span class="line">    <span class="keyword">return</span> &#123; </span><br><span class="line">        setSrc: <span class="function"><span class="keyword">function</span>(<span class="params"> src </span>)</span>&#123; </span><br><span class="line">            imgNode.src = src; </span><br><span class="line">        &#125; </span><br><span class="line">    &#125; </span><br><span class="line">&#125;)(); </span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> proxyImage = (<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line">    <span class="keyword">var</span> img = <span class="keyword">new</span> Image; </span><br><span class="line">    img.onload = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line">        myImage.setSrc( <span class="keyword">this</span>.src ); </span><br><span class="line">    &#125; </span><br><span class="line">    <span class="keyword">return</span> &#123; </span><br><span class="line">        setSrc: <span class="function"><span class="keyword">function</span>(<span class="params"> src </span>)</span>&#123; </span><br><span class="line">            myImage.setSrc( <span class="string">'file:// /C:/Users/svenzeng/Desktop/loading.gif'</span> ); </span><br><span class="line">            img.src = src; </span><br><span class="line">        &#125; </span><br><span class="line">    &#125; </span><br><span class="line">&#125;)(); </span><br><span class="line">proxyImage.setSrc( <span class="string">'http:// imgcache.qq.com/music/photo/k/000GGDys0yA0Nk.jpg'</span> );</span><br></pre></td></tr></table></figure>

<h3 id="合并HTTP"><a href="#合并HTTP" class="headerlink" title="合并HTTP"></a>合并HTTP</h3><p>在 Web 开发中，也许最大的开销就是网络请求。 假设我们在做一个文件同步的功能，当我们选中一个 checkbox 的时候，它对应的文件就会被同 步到另外一台备用服务器上面，</p>
<p>解决方案是，我们可以通过一个代理函数 proxySynchronousFile 来收集一段时间之内的请求， 最后一次性发送给服务器。比如我们等待 2 秒之后才把这 2 秒之内需要同步的文件 ID 打包发给 服务器，如果不是对实时性要求非常高的系统，2 秒的延迟不会带来太大副作用，却能大大减轻 服务器的压力。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> synchronousFile = <span class="function"><span class="keyword">function</span>(<span class="params"> id </span>)</span>&#123; </span><br><span class="line">    <span class="built_in">console</span>.log( <span class="string">'开始同步文件，id 为: '</span> + id ); </span><br><span class="line">&#125;;</span><br><span class="line"><span class="keyword">var</span> proxySynchronousFile = (<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line">    <span class="keyword">var</span> cache = [], <span class="comment">// 保存一段时间内需要同步的 ID </span></span><br><span class="line">        timer; <span class="comment">// 定时器</span></span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params"> id </span>)</span>&#123; </span><br><span class="line">        cache.push( id ); </span><br><span class="line">        <span class="keyword">if</span> ( timer )&#123; <span class="comment">// 保证不会覆盖已经启动的定时器</span></span><br><span class="line">            <span class="keyword">return</span>; </span><br><span class="line">        &#125; </span><br><span class="line">        timer = setTimeout(<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">            synchronousFile( cache.join( <span class="string">','</span> ) ); <span class="comment">// 2 秒后向本体发送需要同步的 ID 集合</span></span><br><span class="line">            clearTimeout( timer ); <span class="comment">// 清空定时器</span></span><br><span class="line">            timer = <span class="literal">null</span>; </span><br><span class="line">            cache.length = <span class="number">0</span>; <span class="comment">// 清空 ID 集合</span></span><br><span class="line">        &#125;, <span class="number">2000</span> ); </span><br><span class="line">    &#125; </span><br><span class="line">&#125;)(); </span><br><span class="line"><span class="keyword">var</span> checkbox = <span class="built_in">document</span>.getElementsByTagName( <span class="string">'input'</span> ); </span><br><span class="line"><span class="keyword">for</span> ( <span class="keyword">var</span> i = <span class="number">0</span>, c; c = checkbox[ i++ ]; )&#123;</span><br><span class="line">    c.onclick = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line">        <span class="keyword">if</span> ( <span class="keyword">this</span>.checked === <span class="literal">true</span> )&#123; </span><br><span class="line">            proxySynchronousFile( <span class="keyword">this</span>.id ); </span><br><span class="line">        &#125; </span><br><span class="line">    &#125; </span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<h2 id="代理意义"><a href="#代理意义" class="headerlink" title="代理意义"></a>代理意义</h2><p>为了说明代理的意义，下面我们引入一个面向对象设计的原则——单一职责原则。 单一职责原则指的是，就一个类（通常也包括对象和函数等）而言，应该仅有一个引起它变 化的原因。如果一个对象承担了多项职责，就意味着这个对象将变得巨大，引起它变化的原因可 能会有多个。面向对象设计鼓励将行为分布到细粒度的对象之中，如果一个对象承担的职责过多， 等于把这些职责耦合到了一起，这种耦合会导致脆弱和低内聚的设计。当变化发生时，设计可能 会遭到意外的破坏。</p>
<p>纵观整个程序，我们并没有改变或者增加 MyImage 的接口，但是通过代理对象，实际上给系 统添加了新的行为。这是符合开放—封闭原则的。给 img 节点设置 src 和图片预加载这两个功能， 被隔离在两个对象里，它们可以各自变化而不影响对方。何况就算有一天我们不再需要预加载， 那么只需要改成请求本体而不是请求代理对象即可。</p>
<h3 id="一致性"><a href="#一致性" class="headerlink" title="一致性"></a>一致性</h3><p>代理对象和本体都对外提供了 setSrc 方法，在客户看来，代理对象和本体 是一致的， 代理接手请求的过程对于用户来说是透明的，用户并不清楚代理和本体的区别，这 样做有两个好处。 1、 用户可以放心地请求代理，他只关心是否能得到想要的结果。 2、在任何使用本体的地方都可以替换成使用代理。</p>
<p>在 Java 等语言中，代理和本体都需要显式地实现同一个接口，一方面接口保证了它们会拥 有同样的方法，另一方面，面向接口编程迎合依赖倒置原则，通过接口进行向上转型，从而避开 编译器的类型检查，代理和本体将来可以被替换使用。</p>
<p>在 JavaScript 这种动态类型语言中，我们有时通过鸭子类型来检测代理和本体是否都实现了 setSrc 方法，另外大多数时候甚至干脆不做检测，全部依赖程序员的自觉性，这对于程序的健壮 性是有影响的。不过对于一门快速开发的脚本语言，这些影响还是在可以接受的范围内，而且我 们也习惯了没有接口的世界</p>
<h2 id="缓存代理"><a href="#缓存代理" class="headerlink" title="缓存代理"></a>缓存代理</h2><p>缓存代理可以为一些开销大的运算结果提供暂时的存储，在下次运算时，如果传递进来的参 数跟之前一致，则可以直接返回前面存储的运算结果。</p>
<p>为了节省示例代码，以及让读者把注意力集中在代理模式上面，这里编写一个简单的求乘积 的程序，请读者自行把它脑补为复杂的计算</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//先创建一个用于求乘积的函数： </span></span><br><span class="line"><span class="keyword">var</span> mult = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line">    <span class="built_in">console</span>.log( <span class="string">'开始计算乘积'</span> ); </span><br><span class="line">    <span class="keyword">var</span> a = <span class="number">1</span>; </span><br><span class="line">    <span class="keyword">for</span> ( <span class="keyword">var</span> i = <span class="number">0</span>, l = <span class="built_in">arguments</span>.length; i &lt; l; i++ )&#123; </span><br><span class="line">        a = a * <span class="built_in">arguments</span>[i]; </span><br><span class="line">    &#125; </span><br><span class="line">    <span class="keyword">return</span> a; </span><br><span class="line">&#125;; </span><br><span class="line">mult( <span class="number">2</span>, <span class="number">3</span> ); <span class="comment">// 输出：6 </span></span><br><span class="line">mult( <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span> ); <span class="comment">// 输出：24 </span></span><br><span class="line"><span class="comment">//现在加入缓存代理函数：</span></span><br><span class="line"><span class="keyword">var</span> proxyMult = (<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line">    <span class="keyword">var</span> cache = &#123;&#125;; </span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line">        <span class="keyword">var</span> args = <span class="built_in">Array</span>.prototype.join.call( <span class="built_in">arguments</span>, <span class="string">','</span> ); </span><br><span class="line">        <span class="keyword">if</span> ( args <span class="keyword">in</span> cache )&#123; </span><br><span class="line">            <span class="keyword">return</span> cache[ args ]; </span><br><span class="line">        &#125; </span><br><span class="line">        <span class="keyword">return</span> cache[ args ] = mult.apply( <span class="keyword">this</span>, <span class="built_in">arguments</span> ); </span><br><span class="line">    &#125; </span><br><span class="line">&#125;)(); </span><br><span class="line">proxyMult( <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span> ); <span class="comment">// 输出：24 </span></span><br><span class="line">proxyMult( <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span> ); <span class="comment">// 输出：24 </span></span><br><span class="line"><span class="comment">/*当我们第二次调用 proxyMult( 1, 2, 3, 4 )的时候，本体 mult 函数并没有被计算，proxyMult</span></span><br><span class="line"><span class="comment">直接返回了之前缓存好的计算结果。</span></span><br><span class="line"><span class="comment">通过增加缓存代理的方式，mult 函数可以继续专注于自身的职责——计算乘积，缓存的功能</span></span><br><span class="line"><span class="comment">是由代理对象实现的。*/</span></span><br></pre></td></tr></table></figure>

<h2 id="高阶函数动态创建"><a href="#高阶函数动态创建" class="headerlink" title="高阶函数动态创建"></a>高阶函数动态创建</h2><p>通过传入高阶函数这种更加灵活的方式，可以为各种计算方法创建缓存代理。现在这些计算方法被当作参数传入一个专门用于创建缓存代理的工厂中， 这样一来，我们就可以为乘法、加 法、减法等创建缓存代理，代码如下</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**************** 计算乘积 *****************/</span> </span><br><span class="line"><span class="keyword">var</span> mult = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line">    <span class="keyword">var</span> a = <span class="number">1</span>; </span><br><span class="line">    <span class="keyword">for</span> ( <span class="keyword">var</span> i = <span class="number">0</span>, l = <span class="built_in">arguments</span>.length; i &lt; l; i++ )&#123; </span><br><span class="line">        a = a * <span class="built_in">arguments</span>[i]; </span><br><span class="line">    &#125; </span><br><span class="line">    <span class="keyword">return</span> a; </span><br><span class="line">&#125;; </span><br><span class="line"><span class="comment">/**************** 计算加和 *****************/</span> </span><br><span class="line"><span class="keyword">var</span> plus = <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line">    <span class="keyword">var</span> a = <span class="number">0</span>; </span><br><span class="line">    <span class="keyword">for</span> ( <span class="keyword">var</span> i = <span class="number">0</span>, l = <span class="built_in">arguments</span>.length; i &lt; l; i++ )&#123; </span><br><span class="line">        a = a + <span class="built_in">arguments</span>[i]; </span><br><span class="line">    &#125; </span><br><span class="line">    <span class="keyword">return</span> a; </span><br><span class="line">&#125;; </span><br><span class="line"><span class="comment">/**************** 创建缓存代理的工厂 *****************/</span> </span><br><span class="line"><span class="keyword">var</span> createProxyFactory = <span class="function"><span class="keyword">function</span>(<span class="params"> fn </span>)</span>&#123; </span><br><span class="line">    <span class="keyword">var</span> cache = &#123;&#125;; </span><br><span class="line">    <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>&#123; </span><br><span class="line">        <span class="keyword">var</span> args = <span class="built_in">Array</span>.prototype.join.call( <span class="built_in">arguments</span>, <span class="string">','</span> ); </span><br><span class="line">        <span class="keyword">if</span> ( args <span class="keyword">in</span> cache )&#123; </span><br><span class="line">            <span class="keyword">return</span> cache[ args ]; </span><br><span class="line">        &#125; </span><br><span class="line">        <span class="keyword">return</span> cache[ args ] = fn.apply( <span class="keyword">this</span>, <span class="built_in">arguments</span> ); </span><br><span class="line">    &#125; </span><br><span class="line">&#125;; </span><br><span class="line"><span class="keyword">var</span> proxyMult = createProxyFactory( mult ), </span><br><span class="line">proxyPlus = createProxyFactory( plus ); </span><br><span class="line">alert ( proxyMult( <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span> ) ); <span class="comment">// 输出：24 </span></span><br><span class="line">alert ( proxyMult( <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span> ) ); <span class="comment">// 输出：24 </span></span><br><span class="line">alert ( proxyPlus( <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span> ) ); <span class="comment">// 输出：10 </span></span><br><span class="line">alert ( proxyPlus( <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span> ) ); <span class="comment">// 输出：10</span></span><br></pre></td></tr></table></figure>


    </div>
    
    
    
    
    <div>
      
        <div>
    
        <div style="text-align:center;color: #ccc;font-size:14px;">-------------本文结束<i class="fa fa-paw"></i>感谢您的阅读-------------</div>
    
</div>

      
    </div>
        <div class="reward-container">
  <div>坚持原创技术分享，您的支持将鼓励我继续创作！</div>
  <button onclick="var qr = document.getElementById('qr'); qr.style.display = (qr.style.display === 'none') ? 'block' : 'none';">
    打赏
  </button>
  <div id="qr" style="display: none;">
      
      <div style="display: inline-block;">
        <img src="/images/alipay.png" alt="Li Yudong 支付宝">
        <p>支付宝</p>
      </div>

  </div>
</div>

        

<div>
<ul class="post-copyright">
  <li class="post-copyright-author">
    <strong>本文作者： </strong>Li Yudong
  </li>
  <li class="post-copyright-link">
    <strong>本文链接：</strong>
    <a href="http://example.com/2021/10/28/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/JavaScript%E7%9A%84%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%EF%BC%88%E4%B8%80%EF%BC%89/" title="JavaScript的设计模式（一）">http://example.com/2021/10/28/设计模式/JavaScript的设计模式（一）/</a>
  </li>
  <li class="post-copyright-license">
    <strong>版权声明： </strong>本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" rel="noopener" target="_blank"><i class="fab fa-fw fa-creative-commons"></i>BY-NC-SA</a> 许可协议。转载请注明出处！
  </li>
</ul>
</div>

    
      <footer class="post-footer">
          <div class="post-tags">
              <a href="/tags/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/" rel="tag"># 设计模式</a>
          </div>
    
        

    
        
    <div class="post-nav">
      <div class="post-nav-item">
    <a href="/2021/10/24/node/%E5%89%8D%E7%AB%AF%E8%A7%86%E5%9B%BE%E6%A8%A1%E6%9D%BF/" rel="prev" title="前端视图模板">
      <i class="fa fa-chevron-left"></i> 前端视图模板
    </a></div>
      <div class="post-nav-item">
    <a href="/2021/10/31/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/JavaScript%E7%9A%84%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%EF%BC%88%E4%BA%8C%EF%BC%89/" rel="next" title="JavaScript的设计模式（二）">
      JavaScript的设计模式（二） <i class="fa fa-chevron-right"></i>
    </a></div>
    </div>
      </footer>
    
  </article>
  
  
  



          </div>
          

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
          <div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#前言"><span class="nav-number">1.</span> <span class="nav-text">前言</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#多态"><span class="nav-number">1.1.</span> <span class="nav-text">多态</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#封装"><span class="nav-number">1.2.</span> <span class="nav-text">封装</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#继承"><span class="nav-number">1.3.</span> <span class="nav-text">继承</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#AOP"><span class="nav-number">1.4.</span> <span class="nav-text">AOP</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#高阶函数"><span class="nav-number">1.5.</span> <span class="nav-text">高阶函数</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#currying"><span class="nav-number">1.5.1.</span> <span class="nav-text">currying</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#uncurrying"><span class="nav-number">1.5.2.</span> <span class="nav-text">uncurrying</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#分时函数"><span class="nav-number">1.5.3.</span> <span class="nav-text">分时函数</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#单例模式"><span class="nav-number">2.</span> <span class="nav-text">单例模式</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#透明单例"><span class="nav-number">2.1.</span> <span class="nav-text">透明单例</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#代理实现"><span class="nav-number">2.2.</span> <span class="nav-text">代理实现</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#惰性单例"><span class="nav-number">2.3.</span> <span class="nav-text">惰性单例</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#策略模式"><span class="nav-number">3.</span> <span class="nav-text">策略模式</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#简单实现"><span class="nav-number">3.1.</span> <span class="nav-text">简单实现</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#表单验证"><span class="nav-number">3.2.</span> <span class="nav-text">表单验证</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#优缺点"><span class="nav-number">3.3.</span> <span class="nav-text">优缺点</span></a></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#代理模式"><span class="nav-number">4.</span> <span class="nav-text">代理模式</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#代理分类"><span class="nav-number">4.1.</span> <span class="nav-text">代理分类</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#图片预加载"><span class="nav-number">4.1.1.</span> <span class="nav-text">图片预加载</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#合并HTTP"><span class="nav-number">4.1.2.</span> <span class="nav-text">合并HTTP</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#代理意义"><span class="nav-number">4.2.</span> <span class="nav-text">代理意义</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#一致性"><span class="nav-number">4.2.1.</span> <span class="nav-text">一致性</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#缓存代理"><span class="nav-number">4.3.</span> <span class="nav-text">缓存代理</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#高阶函数动态创建"><span class="nav-number">4.4.</span> <span class="nav-text">高阶函数动态创建</span></a></li></ol></li></ol></div>
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="Li Yudong"
      src="/images/author.jpg">
  <p class="site-author-name" itemprop="name">Li Yudong</p>
  <div class="site-description" itemprop="description"></div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/archives/">
        
          <span class="site-state-item-count">95</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/categories/">
          
        <span class="site-state-item-count">14</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
            <a href="/tags/">
          
        <span class="site-state-item-count">18</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>
  <div class="links-of-author motion-element">
      <span class="links-of-author-item">
        <a href="https://github.com/tiarmor1" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;tiarmor1" rel="noopener" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a>
      </span>
      <span class="links-of-author-item">
        <a href="mailto:1157019137@qq.com" title="E-Mail → mailto:1157019137@qq.com" rel="noopener" target="_blank"><i class="fa fa-envelope fa-fw"></i>E-Mail</a>
      </span>
  </div>
  <div class="cc-license motion-element" itemprop="license">
    <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" class="cc-opacity" rel="noopener" target="_blank"><img src="/images/cc-by-nc-sa.svg" alt="Creative Commons"></a>
  </div>



      </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        

<div class="copyright">
  
  &copy; 2020 – 
  <span itemprop="copyrightYear">2022</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">Li Yudong</span>
</div>

        








      </div>
    </footer>
  </div>

  
  
  <script color='0,0,0' opacity='0.5' zIndex='-1' count='150' src="/lib1/canvas-nest/canvas-nest.min.js"></script>
  <script src="/lib1/anime.min.js"></script>
  <script src="/lib1/velocity/velocity.min.js"></script>
  <script src="/lib1/velocity/velocity.ui.min.js"></script>

<script src="/js/utils.js"></script>

<script src="/js/motion.js"></script>


<script src="/js/schemes/pisces.js"></script>


<script src="/js/next-boot.js"></script>




  




  
<script src="/js/local-search.js"></script>













  

  

  
   <canvas class="fireworks" style="position: fixed;left: 0;top: 0;z-index: 1; pointer-events: none;" ></canvas> 
   <script type="text/javascript" src="//cdn.bootcss.com/animejs/2.2.0/anime.min.js"></script> 
   <script type="text/javascript" src="/js/fireworks.js"></script>
  
</body>
</html>

